/************************************************************
 * Name: RedVMRegUpdateFilter.cpp
 *
 * Description:
 *    Elminate redudent Virtual Machine Register updates.
 *
 * History:
 *    Created: 
 *          gxf. Nov. 22nd, 2008. 
 *
 *    Last Modified:
 *
 *
 ************************************************************/

#include "crossbit/RedVMRegUpdateFilter.h"
#include "crossbit/VInst.h"
#include "crossbit/VBlock.h"
#include "crossbit/GLOBAL.h"
#include "crossbit/UTIL.h"
#include <algorithm>

using namespace crossbit;

XTUint32 RedVMRegUpdateFilter::optCnt = 0;

XTUint32 RedVMRegUpdateFilter::run(VBlock* vb)
{
    VBlock::vb_iterator Itr = vb->begin(); 
    VBlock::vb_iterator End = vb->end(); 

    bool notChanged = false;
    XTRegNum refReg = 0;

    while(notChanged == false)
    { 
        // Reset conditions and data structures
        UTIL::XTMemset(table, 0, sizeof(table));

        Itr = vb->begin(); 
        notChanged = true;

#define MARK_UPDATE_REG(regNum)    \
    refReg = inst->getOperand(regNum).reg;  \
    if(table[refReg] != NULL){            \
        notChanged = false; \
    }   \
    table[refReg] = inst;
//        DeleteInst(table[refReg], vb, Itr); \

#define MARK_REFER_REG(regNum)         \
    refReg = inst->getOperand(regNum).reg;  \
    table[refReg] = NULL;          

        while (Itr != vb -> end())
        {
            VInst* inst = *Itr;
            switch(inst -> getOpcode())
            {
                case OP_LI: 
                    MARK_UPDATE_REG(2);
                    break;
                case OP_GET:
                    MARK_UPDATE_REG(2);
                    break;
                case OP_PUT:
                    MARK_REFER_REG(1);
                    break;
                case OP_MOV:
                    MARK_REFER_REG(1);
                    MARK_UPDATE_REG(2);
                    break;
                case OP_LD:
                    MARK_REFER_REG(1);
                    MARK_UPDATE_REG(4);
                    break;
                case OP_ST:
                    MARK_REFER_REG(1);
                    MARK_REFER_REG(3);
                    break;
                case OP_SEXT:
                case OP_ZEXT:
                    MARK_REFER_REG(1);
                    MARK_UPDATE_REG(3);
                    break;
                case OP_NOT:
                    MARK_REFER_REG(1);
                    MARK_UPDATE_REG(2);
                    break;
                case OP_ADD:
                case OP_SUB:
                case OP_AND:
                case OP_XOR:
                case OP_OR:
                case OP_SLL:
                case OP_SRL:
                case OP_SRA:
                /* TODO: Seems not supported by translator yet
                case OP_SLLV:
                case OP_SRLV:
                case OP_SRAV:
                */
                    MARK_REFER_REG(1);
                    MARK_REFER_REG(2);
                    MARK_UPDATE_REG(3);
                    break;
                case OP_MUL:
                case OP_MULU:
                case OP_DIV:
                case OP_DIVU:
                    MARK_REFER_REG(1);
                    MARK_REFER_REG(2);
                    MARK_UPDATE_REG(3);
                    MARK_UPDATE_REG(4);
                    break;
                case OP_CMP:
                    MARK_REFER_REG(2);
                    MARK_REFER_REG(3);
                    MARK_UPDATE_REG(4);
                    break;
                case OP_JMP:
                    MARK_REFER_REG(1);
                    break;
                case OP_BRANCH:
                    MARK_REFER_REG(2);
                    MARK_REFER_REG(3);
                    MARK_REFER_REG(4);
                    break;
                    // Nothing about reg
                case OP_SYSCALL:
                case OP_CALL:
                case OP_HALT:
                    break;
                default:
                // Unsupported inst in
               // assert(0);
                break;
            } // Swtich
            Itr++;
/*            std::cout << "Current Table:" << std::endl; 
            int cnt = 0;
            while (cnt < GLOBAL::MAX_VREG)
            {
                if (table[cnt] != NULL)
                    std::cout << cnt << ":" << *(table[cnt]) << std::endl;
                cnt++;
            }
            std::cout << "Current code:" << std::endl;
            vb->print(std::cout);
            std::cout << std::endl;
            std::cout << "NotChanged = " << notChanged << std::endl;
*/        }

/*        std::cout << "Table entry to delete:" << std::endl; 
        int regcnt = 0;
        while (regcnt < GLOBAL::MAX_VREG)
        {
            if (table[regcnt] != NULL)
                std::cout << regcnt << ":" << *(table[regcnt]) << std::endl;
            regcnt++;
        }
*/
        int cnt = 0;
        while (cnt < GLOBAL::MAX_VREG)
        {
            if (table[cnt] != NULL)
            {
                DeleteInst(table[cnt], vb);
                notChanged = false;
            }
            cnt++;
        }
//        std::cout << "NotChanged = " << notChanged << std::endl;
    }
}

// Delete VInst from VBlock (search according to key);
void RedVMRegUpdateFilter::DeleteInst(VInst* curInst, VBlock* pBlock) 
{
    VBlock::vb_iterator pos;
    pos = std::find(pBlock -> begin(), pBlock -> end(), curInst);

    if (pos != pBlock -> end()){
        pos = pBlock -> erase(pos);
//        std::cout << "Deleted inst: " << *curInst << std::endl;
        delete curInst;
        curInst = NULL;
        optCnt++;
    }
    else{
        assert(0);
    }
}

// Delete VInst from VBlock (search according to key);
void RedVMRegUpdateFilter::DeleteInst(VInst* curInst, VBlock* pBlock, VBlock::vb_iterator& Itr)
{
    VBlock::vb_iterator pos;
    pos = std::find(pBlock -> begin(), pBlock -> end(), curInst);

    if (pos != pBlock -> end()){
        // Get the distance of iterators
//        iterator_traits<VBlock::vb_iterator>::difference_type dist;
        int dist;
        dist = distance(pos, Itr);

        VInst * refInst = *pos;
        pos = pBlock -> erase(pos);
        delete refInst;
        refInst = NULL;
        optCnt++;

       // Itr = pos + (dist - 1);
		Itr = pos;
		if ( dist -1 > 0 ) {
			for( XTInt32 i = 0 ; i < dist - 1 ; i++ ) {
				Itr ++;
			}
		} else {
			for( XTInt32 i = 0 ; i > dist - 1 ; i-- ) {
				Itr --;
			}
		}
    }
    else{
        assert(0);
    }
}

